package com.kuafu.latte.exchange;

import io.netty.channel.Channel;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DefaultPackage {

    private static final Map<String, DefaultPackage> Packages = new ConcurrentHashMap<String, DefaultPackage>();
    private final String id;
    private final Channel channel;
    private final int timeout;
    private final Message request;
    private volatile Message response;

    private final Lock lock = new ReentrantLock();
    private final Condition done = lock.newCondition();

    public DefaultPackage(Channel channel, Message request, int timeout) {
        this.channel = channel;
        this.request = request;
        this.id = request.getId();
        this.timeout = timeout;
        Packages.put(id, this);
    }

    public static void received(Channel channel, Message response) {
        try {
            DefaultPackage pkg = Packages.remove(response.getId());
            if (pkg != null) {
                pkg.doReceived(response);
            } else {
            }
        } finally {
            //CHANNELS.remove(response.getId());
        }
    }
    private void doReceived(Message res) {
        lock.lock();
        try {
            response = res;
            if (done != null) {
                done.signal();
            }
        } finally {
            lock.unlock();
        }
    }

    public Object get() throws RuntimeException {
        if (!isDone()) {
            long start = System.currentTimeMillis();
            lock.lock();
            try {
                while (!isDone()) {
                    done.await(timeout, TimeUnit.MILLISECONDS);
                    if (isDone() || System.currentTimeMillis() - start > timeout) {
                        break;
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
            if (!isDone()) {
                throw new RuntimeException("receive time out");
            }
        }
        return response.getBody();
    }
    public boolean isDone() {
        return response != null;
    }
}
